/* 
 * Copyright 2006-2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.anyline.config.db.impl;


import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.Table;

import org.anyline.config.db.SQL;
import org.anyline.config.db.SQLCreater;
import org.anyline.config.db.run.RunSQL;
import org.anyline.config.db.run.TableRunSQLImpl;
import org.anyline.config.db.run.TextRunSQLImpl;
import org.anyline.config.db.run.XMLRunSQLImpl;
import org.anyline.config.db.sql.auto.TableSQL;
import org.anyline.config.db.sql.auto.TextSQL;
import org.anyline.config.db.sql.xml.XMLSQL;
import org.anyline.config.http.ConfigStore;
import org.anyline.dao.PrimaryCreater;
import org.anyline.entity.BasicEntity;
import org.anyline.entity.DataRow;
import org.anyline.util.BasicUtil;
import org.anyline.util.BeanUtil;
import org.anyline.util.ConfigTable;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;


/**
 * SQL生成 子类主要实现与分页相关的SQL 以及disKey
 * @author  
 * @since   1.0
 */

public abstract class BasicSQLCreaterImpl implements SQLCreater{
	protected static Logger LOG = Logger.getLogger(BasicSQLCreaterImpl.class);
	public String disKeyFr = "";
	public String disKeyTo = "";

	@Autowired(required=false)
	private PrimaryCreater primaryCreater;
	/**
	 * 创建查询SQL
	 */
	@Override
	public RunSQL createQueryRunSQL(SQL sql, ConfigStore configs, String ... conditions){
		RunSQL run = null;
		if(sql instanceof TableSQL){
			run = new TableRunSQLImpl();
		}else if(sql instanceof XMLSQL){
			run = new XMLRunSQLImpl();
		}else if(sql instanceof TextSQL){
			run = new TextRunSQLImpl();
		}
		
		run.setCreater(this);
		run.setSql(sql);
		run.setConfigStore(configs);
		run.addConditions(conditions);
		run.init();

		return run;
	}
	@Override
	public RunSQL createExecuteRunSQL(SQL sql, ConfigStore configs, String ... conditions){
		RunSQL run = null;
		if(sql instanceof XMLSQL){
			run = new XMLRunSQLImpl();
		}else if(sql instanceof TextSQL){
			run = new TextRunSQLImpl();
		}
		run.setCreater(this);
		run.setSql(sql);
		run.setConfigStore(configs);
		run.addConditions(conditions);
		run.init();
		return run;
	}
	@Override
	public RunSQL createDeleteRunSQL(String dest, Object obj, String ... columns){
		if(null == obj){
			return null;
		}
		RunSQL run = null;
		if(null == dest){
			dest = getDataSource(obj);
		}
		if(obj instanceof DataRow){
			run = createDeleteRunSQLFromDataRow(dest, (DataRow)obj, columns);
		}else if(obj instanceof BasicEntity){
			run = createDeleteRunSQLFromEntity(dest, (BasicEntity)obj, columns);
		}
		return run;
	}
	
	private RunSQL createDeleteRunSQLFromDataRow(String dest, DataRow obj, String ... columns){
		TableRunSQLImpl run = new TableRunSQLImpl();
		run.getBuilder().append("DELETE FROM ").append(dest)
		.append(" WHERE ").append(getDisKeyFr()).append(obj.getPrimaryKey()).append(getDisKeyTo())
		.append("=?");
		run.addValue(obj.getPrimaryValue());
		return run;
	}

	private RunSQL createDeleteRunSQLFromEntity(String dest, BasicEntity obj, String ... columns){
		TableRunSQLImpl run = new TableRunSQLImpl();
		run.getBuilder().append("DELETE FROM ").append(dest)
		.append(" WHERE ").append(getDisKeyFr()).append(getPrimaryKey(obj)).append(getDisKeyTo())
		.append("=?");
		run.addValue(getPrimaryValue(obj));
		return run;
	}

	@Override
	public String getPrimaryKey(Object obj){
		if(null == obj){
			return null;
		}
		if(obj instanceof DataRow){
			return ((DataRow)obj).getPrimaryKey();
		}else{
			return BeanUtil.getPrimaryKey(obj.getClass());
		}
	}
	@Override
	public Object getPrimaryValue(Object obj){
		if(null == obj){
			return null;
		}
		if(obj instanceof DataRow){
			return ((DataRow)obj).getPrimaryValue();
		}else{
			return BeanUtil.getPrimaryValue(obj);
		}
	}
	/**
	 * 基础查询SQL
	 * RunSQL 反转调用
	 */
	@Override
	public String parseBaseQueryTxt(RunSQL run){
		return run.getBuilder().toString();
	}
	/**
	 * 求总数SQL
	 * RunSQL 反转调用
	 * @param txt
	 * @return
	 */
	@Override
	public String parseTotalQueryTxt(RunSQL run){
		String sql = "SELECT COUNT(0) AS CNT FROM (\n" + run.getBuilder().toString() +"\n) AS F";
		sql = sql.replaceAll("WHERE\\s*1=1\\s*AND", "WHERE");
		return sql;
	}
	
	


	@Override
	public String getDisKeyFr(){
		return disKeyFr;
	}
	@Override
	public String getDisKeyTo(){
		return disKeyTo;
	}

	@Override
	public RunSQL createInsertTxt(String dest, Object obj, boolean checkPrimary, String ... columns){
		if(null == obj){
			return null;
		}
		if(null == dest){
			dest = getDataSource(obj);
		}
		if(obj instanceof DataRow){
			return createInsertTxtFromDataRow(dest,(DataRow)obj,checkPrimary, columns);
		}
		if(obj instanceof BasicEntity){
			return createInsertTxtFromEntity(dest,(BasicEntity)obj,checkPrimary, columns);	
		}
		return null;
	}
	@Override
	public String getDataSource(Object obj){
		if(null == obj){
			return null;
		}
		String result = "";
		if(obj instanceof DataRow){
			DataRow row = (DataRow)obj;
			String author = row.getAuthor();
			String table = row.getTable();
			if(null != author){
				result = getDisKeyFr() + author +  getDisKeyTo() +".";
			}
			if(null == table){
				return null;
			}
			result = result + getDisKeyFr() + table + getDisKeyTo();
		}else{
			try{
				Annotation annotation = obj.getClass().getAnnotation(Table.class);			//提取Table注解
				Method method = annotation.annotationType().getMethod("name");				//引用name方法
				result = (String)method.invoke(annotation);									//执行name方法返回结果
				result = result.replace(getDisKeyFr(), "").replace(getDisKeyTo(),"");
			}catch(NoClassDefFoundError e){
				LOG.error(e);
			}catch(Exception e){
				LOG.error(e);
			}
		}
		return result;
	}
	private RunSQL createInsertTxtFromDataRow(String dest, DataRow row, boolean checkPrimary, String ... columns){
		RunSQL run = new TableRunSQLImpl();
		StringBuilder sql = new StringBuilder();
		List<Object> values = new ArrayList<Object>();
		if(BasicUtil.isEmpty(dest)){
			throw new RuntimeException("未指定表");
		}
		StringBuilder param = new StringBuilder();
		if(row.hasPrimaryKeys() && ConfigTable.getBoolean("AUTO_CREATE_PRIMARY_KEY") && BasicUtil.isEmpty(row.get(row.getPrimaryKey()))){
			String pk = row.getPrimaryKey();
			if(null == pk){
				pk = ConfigTable.getString("DEFAULT_PRIMARY_KEY");
			}
			row.put(pk, primaryCreater.createPrimary(dest, pk, null));
		}
		/*确定需要插入的列*/
		List<String> keys = confirmInsertColumns(dest, row, columns);
		if(null == keys || keys.size() == 0){
			throw new RuntimeException("未指定列");
		}
		sql.append("INSERT INTO ").append(dest);
		sql.append("(");
		param.append(") VALUES (");
		
		int size = keys.size();
		for(int i=0; i<size; i++){
			String key = keys.get(i);
			Object value = row.get(key);
			sql.append(getDisKeyFr()).append(key).append(getDisKeyTo());
			if(null != value && value.toString().startsWith("{") && value.toString().endsWith("}")){
				value = value.toString().replace("{", "").replace("}", "");
				param.append(value);
			}else{
				param.append("?");
				values.add(value);
			}
			if(i<size-1){
				sql.append(",");
				param.append(",");
			}
		}
		param.append(")");
		sql.append(param);
		run.setBuilder(sql);
		run.addValues(values);
		return run;
	}
	private RunSQL createInsertTxtFromEntity(String dest, BasicEntity entity, boolean checkPrimary, String ... columns){
		RunSQL run = new TableRunSQLImpl();
		StringBuilder sql = new StringBuilder();
		List<Object> values = new ArrayList<Object>();
		

		if(null == dest){
			dest = entity.getDataSource();
		}
		if(BasicUtil.isEmpty(dest)){
			throw new RuntimeException("未指定表");
		}
		
		
		/*确定需要更新的列*/
		List<String> keys = confirmInsertColumns(dest, entity, columns);
		if(null == keys || keys.size() == 0){
			throw new RuntimeException("未指定列");
		}
		sql.append("INSERT INTO ").append(dest);
		sql.append("(");
		int size = keys.size();
		for(int i=0; i<size; i++){
			sql.append(getDisKeyFr()).append(keys.get(i)).append(getDisKeyTo());
			if(i<size-1){
				sql.append(",");
			}
		}
		sql.append(") VALUES (");
		for(int i=0; i<size; i++){
			sql.append("?");
			if(i<size-1){
				sql.append(",");
			}
			values.add(entity.getValueByColumn(keys.get(i)));
		}
		sql.append(")");
		run.setBuilder(sql);
		run.addValues(values);
		return run;
	}

	@Override
	public RunSQL createUpdateTxt(String dest, Object obj, boolean checkPrimary, String ... columns){
		if(null == obj){
			return null;
		}
		if(null == dest){
			dest = getDataSource(obj);
		}
		if(obj instanceof DataRow){
			return createUpdateTxtFromDataRow(dest,(DataRow)obj,checkPrimary, columns);
		}
		if(obj instanceof BasicEntity){
			return createUpdateTxtFromEntity(dest,(BasicEntity)obj,checkPrimary, columns);
		}
		return null;
	}

	private RunSQL createUpdateTxtFromEntity(String dest, BasicEntity entity, boolean checkPrimary, String ... columns){
		RunSQL run = new TableRunSQLImpl();
//		if(null == entity){
		
//			throw new RuntimeException("更新空数据");
//		}
//		if(null == dest){
//			dest = entity.getDataSource();
//		}
//		if(BasicUtil.isEmpty(dest)){
//			throw new RuntimeException("未指定表");
//		}
//		List<String> primaryKeys = entity.getPrimaryKeys();
//		if(BasicUtil.isEmpty(true,primaryKeys)){
//			throw new RuntimeException("未指定主键");
//		}
//		
//		entity.processBeforeSave();	//保存之前预处理
//		
//		StringBuilder sql = new StringBuilder();
//		List<Object> values = new ArrayList<Object>();
//		/*确定需要更新的列*/
//		List<String> keys = confirmUpdateColumns(dest, entity, propertys);
//		/*不更新主键*/
//		for(String key:primaryKeys){
//			keys.remove(key);
//		}
//		
//		if(BasicUtil.isEmpty(true,keys)){
//			throw new RuntimeException("未指定更新列");
//		}
//
//		/*构造SQL*/
//		sql.append("UPDATE ").append(dest);
//		sql.append(" SET").append(SQLCreater.BR_TAB);
//		int size = keys.size();
//		for(int i=0; i<size; i++){
//			String key = keys.get(i);
//			sql.append(getDisKeyFr()).append(key).append(getDisKeyTo()).append(" = ?").append(SQLCreater.BR_TAB);
//			values.add(entity.getValueByColumn(key));
//			if(i<size-1){
//				sql.append(",");
//			}
//		}
//		//sql.append(SQL.BR);
//		sql.append("\nWHERE 1=1").append(SQLCreater.BR_TAB);
//		for(String primary:primaryKeys){
//			sql.append(" AND ").append(getDisKeyFr()).append(primary).append(getDisKeyTo()).append(" = ?");
//			values.add(entity.getValueByColumn(primary));
//		}
//		entity.processBeforeDisplay();	//显示之前预处理
		return run;
	}
	private RunSQL createUpdateTxtFromDataRow(String dest, DataRow row, boolean checkPrimary, String ... columns){
		RunSQL run = new TableRunSQLImpl();
		StringBuilder sql = new StringBuilder();
		List<Object> values = new ArrayList<Object>();
		/*确定需要更新的列*/
		List<String> keys = confirmUpdateColumns(dest, row, columns);
		String primaryKey = row.getPrimaryKey();
		/*不更新主键*/
		keys.remove(primaryKey);
		if(BasicUtil.isEmpty(true,keys)){
			throw new RuntimeException("未指定更新列");
		}
		/*构造SQL*/
		sql.append("UPDATE ").append(dest);
		sql.append(" SET").append(SQLCreater.BR_TAB);
		int size = keys.size();
		for(int i=0; i<size; i++){
			String key = keys.get(i);
			sql.append(getDisKeyFr()).append(key).append(getDisKeyTo()).append(" = ?").append(SQLCreater.BR_TAB);
			values.add(row.get(key));
			if(i<size-1){
				sql.append(",");
			}
		}
		sql.append(SQLCreater.BR);
		sql.append("\nWHERE 1=1").append(SQLCreater.BR_TAB);
			sql.append(" AND ").append(getDisKeyFr()).append(primaryKey).append(getDisKeyTo()).append(" = ?");
			values.add(row.getPrimaryValue());
		run.setBuilder(sql);
		run.addValues(values);
		return run;
	}
	/**
	 * 确认需要插入的列
	 * @param row
	 * @param columns
	 * @return
	 */
	private List<String> confirmInsertColumns(String dst, DataRow row, String ... columns){
		List<String> keys = null;/*确定需要插入的列*/
		if(null == row){
			return new ArrayList<String>();
		}
		boolean each = true;//是否需要从row中查找列
		List<String> mastKeys = new ArrayList<String>();		//必须插入列
		List<String> disKeys = new ArrayList<String>();			//必须不插入列
		List<String> factKeys = new ArrayList<String>();		//根据是否空值

		if(null != columns && columns.length>0){
			each = false;
			keys = new ArrayList<String>();
			for(String column:columns){
				if(BasicUtil.isEmpty(column)){
					continue;
				}
				if(column.startsWith("+")){
					column = column.replace("+", "");
					mastKeys.add(column);
					each = true;
				}else if(column.startsWith("-")){
					column = column.replace("-", "");
					disKeys.add(column);
					each = true;
				}else if(column.startsWith("?")){
					column = column.replace("?", "");
					factKeys.add(column);
					each = true;
				}
				keys.add(column);
			}
		}
		if(each){
//			if(!dst.equals(ConfigTable.getString("CLIENT_TRACE_TABLE")) && !dst.contains("CLIENT_TRACE")){
//				ClientTrace client = (ClientTrace)row.getClientTrace();
//				if(null != client){
//					row.put("REG_IP", client.getRemoteIP());
//					row.put("REG_CLIENT_CD", client.getCd());
//				}
//			}
			keys = row.keys();
			//是否插入null及""列
			boolean isInsertNullColumn = ConfigTable.getBoolean("IS_INSERT_NULL_COLUMN",false);
			boolean isInsertEmptyColumn = ConfigTable.getBoolean("IS_INSERT_EMPTY_COLUMN",false);
			int size = keys.size();
			for(int i=size-1;i>=0; i--){
				String key = keys.get(i);
				if(mastKeys.contains(key)){
					//必须插入
					continue;
				}
				if(disKeys.contains(key)){
					keys.remove(key);
					continue;
				}
				
				Object value = row.get(key);
				if(null == value){
					if(factKeys.contains(key)){
						keys.remove(key);
						continue;
					}	
					if(!isInsertNullColumn){
						keys.remove(i);
						continue;
					}
				}else if("".equals(value.toString().trim())){
					if(factKeys.contains(key)){
						keys.remove(key);
						continue;
					}	
					if(!isInsertEmptyColumn){
						keys.remove(i);
						continue;
					}
				}
				
			}
		}
		return keys;
	}
	/**
	 * 确认需要插入的列
	 * @param entity
	 * @param columns
	 * @return
	 */
	private List<String> confirmInsertColumns(String dst, BasicEntity entity, String ... propertys){
		List<String> keys = null;/*确定需要插入的列*/
		if(null == entity){
			return new ArrayList<String>();
		}
		boolean each = true;//是否需要从row中查找列
		List<String> mastKeys = new ArrayList<String>();		//必须插入列
		List<String> disKeys = new ArrayList<String>();			//必须不插入列
		List<String> factKeys = new ArrayList<String>();		//根据是否空值

		if(null != propertys && propertys.length>0){
			each = false;
			keys = new ArrayList<String>();
			for(String property:propertys){
				if(BasicUtil.isEmpty(property)){
					continue;
				}
				if(property.startsWith("+")){
					property = property.replace("+", "");
					String column = entity.getColumnByProperty(property);
					mastKeys.add(column);
					each = true;
				}else if(property.startsWith("-")){
					property = property.replace("-", "");
					String column = entity.getColumnByProperty(property);
					disKeys.add(column);
					each = true;
				}else if(property.startsWith("?")){
					property = property.replace("?", "");
					String column = entity.getColumnByProperty(property);
					factKeys.add(column);
					each = true;
				}
				keys.add(entity.getColumnByProperty(property));
			}
		}
		if(each){
//			if(!dst.equals(ConfigTable.getString("CLIENT_TRACE_TABLE")) && !dst.contains("CLIENT_TRACE")){
//				ClientTrace client = (ClientTrace)entity.getClientTrace();
//				if(null != client){
//					entity.setRegIp(client.getRemoteIP());
//					entity.setRegClientCd(client.getCd());
//				}
//			}
			keys = entity.getColumns(true, false);
			//是否插入null及""列
			boolean isInsertNullColumn = ConfigTable.getBoolean("IS_INSERT_NULL_COLUMN",false);
			boolean isInsertEmptyColumn = ConfigTable.getBoolean("IS_INSERT_EMPTY_COLUMN",false);
			int size = keys.size();
			for(int i=size-1;i>=0; i--){
				String key = keys.get(i);
				if(mastKeys.contains(key)){
					//必须插入
					continue;
				}
				if(disKeys.contains(key)){
					keys.remove(key);
					continue;
				}
				
				Object value = BeanUtil.getValueByColumn(entity, key);
				if(null == value){
					if(factKeys.contains(key)){
						keys.remove(key);
						continue;
					}	
					if(!isInsertNullColumn){
						keys.remove(i);
						continue;
					}
				}else if("".equals(value.toString().trim())){
					if(factKeys.contains(key)){
						keys.remove(key);
						continue;
					}	
					if(!isInsertEmptyColumn){
						keys.remove(i);
						continue;
					}
				}
				
			}
		}
		return keys;
	}
	/**
	 * 确认需要更新的列
	 * @param row
	 * @param columns
	 * @return
	 */
	private List<String> confirmUpdateColumns(String dst, DataRow row, String ... columns){
		List<String> keys = null;/*确定需要更新的列*/
		if(null == row){
			return new ArrayList<String>();
		}
		boolean each = true;//是否需要从row中查找列
		List<String> mastKeys = new ArrayList<String>();		//必须更新列
		List<String> disKeys = new ArrayList<String>();			//必须不更新列
		List<String> factKeys = new ArrayList<String>();		//根据是否空值

		if(null != columns && columns.length>0){
			each = false;
			keys = new ArrayList<String>();
			for(String column:columns){
				if(BasicUtil.isEmpty(column)){
					continue;
				}
				if(column.startsWith("+")){
					column = column.replace("+", "");
					mastKeys.add(column);
					each = true;
				}else if(column.startsWith("-")){
					column = column.replace("-", "");
					disKeys.add(column);
					each = true;
				}else if(column.startsWith("?")){
					column = column.replace("?", "");
					factKeys.add(column);
					each = true;
				}
				keys.add(column);
			}
		}
		if(each){
//			if(!dst.equals(ConfigTable.getString("CLIENT_TRACE_TABLE")) && !dst.contains("CLIENT_TRACE")){
//				ClientTrace client = (ClientTrace)row.getClientTrace();
//				if(null != client){
//					row.put("UPT_IP", client.getRemoteIP());
//					row.put("UPT_CLIENT_CD", client.getCd());
//				}
//			}
			keys = row.keys();
			//是否更新null及""列
			boolean isUpdateNullColumn = ConfigTable.getBoolean("IS_UPDATE_NULL_COLUMN",false);
			boolean isUpdateEmptyColumn = ConfigTable.getBoolean("IS_UPDATE_EMPTY_COLUMN",false);
			int size = keys.size();
			for(int i=size-1;i>=0; i--){
				String key = keys.get(i);
				if(mastKeys.contains(key)){
					//必须更新
					continue;
				}
				if(disKeys.contains(key)){
					keys.remove(key);
					continue;
				}
				
				Object value = row.get(key);
				if(null == value){
					if(factKeys.contains(key)){
						keys.remove(key);
						continue;
					}	
					if(!isUpdateNullColumn){
						keys.remove(i);
						continue;
					}
				}else if("".equals(value.toString().trim())){
					if(factKeys.contains(key)){
						keys.remove(key);
						continue;
					}	
					if(!isUpdateEmptyColumn){
						keys.remove(i);
						continue;
					}
				}
				
			}
		}
		return keys;
	}
	/**
	 * 确认需要更新的列
	 * @param entity
	 * @param columns
	 * @return
	 */
	private List<String> confirmUpdateColumns(String dst, BasicEntity entity, String ... propertys){
		List<String> keys = null;/*确定需要更新的列*/
		if(null == entity){
			return new ArrayList<String>();
		}
		boolean each = true;//是否需要从row中查找列
		List<String> mastKeys = new ArrayList<String>();		//必须更新列
		List<String> disKeys = new ArrayList<String>();			//必须不更新列
		List<String> factKeys = new ArrayList<String>();		//根据是否空值

		if(null != propertys && propertys.length>0){
			each = false;
			keys = new ArrayList<String>();
			for(String property:propertys){
				if(BasicUtil.isEmpty(property)){
					continue;
				}
				if(property.startsWith("+")){
					property = property.replace("+", "");
					String column = entity.getColumnByProperty(property);
					mastKeys.add(column);
					each = true;
				}else if(property.startsWith("-")){
					property = property.replace("-", "");
					String column = entity.getColumnByProperty(property);
					disKeys.add(column);
					each = true;
				}else if(property.startsWith("?")){
					property = property.replace("?", "");
					String column = entity.getColumnByProperty(property);
					factKeys.add(column);
					each = true;
				}
				keys.add(entity.getColumnByProperty(property));
			}
		}
		if(each){
//			if(!dst.equals(ConfigTable.getString("CLIENT_TRACE_TABLE")) && !dst.contains("CLIENT_TRACE")){
//				ClientTrace client = (ClientTrace)entity.getClientTrace();
//				if(null != client){
//					entity.setUptIp(client.getRemoteIP());
//					entity.setUptClientCd(client.getCd());
//				}
//			}
			keys = entity.getColumns(false, true);
			//是否更新null及""列
			boolean isUpdateNullColumn = ConfigTable.getBoolean("IS_UPDATE_NULL_COLUMN",false);
			boolean isUpdateEmptyColumn = ConfigTable.getBoolean("IS_UPDATE_EMPTY_COLUMN",false);
			int size = keys.size();
			for(int i=size-1;i>=0; i--){
				String key = keys.get(i);
				if(mastKeys.contains(key)){
					//必须更新
					continue;
				}
				if(disKeys.contains(key)){
					keys.remove(key);
					continue;
				}
				
				Object value = BeanUtil.getValueByColumn(entity, key);
				if(null == value){
					if(factKeys.contains(key)){
						keys.remove(key);
						continue;
					}	
					if(!isUpdateNullColumn){
						keys.remove(i);
						continue;
					}
				}else if("".equals(value.toString().trim())){
					if(factKeys.contains(key)){
						keys.remove(key);
						continue;
					}	
					if(!isUpdateEmptyColumn){
						keys.remove(i);
						continue;
					}
				}
				
			}
		}
		return keys;
	}

}
